package golf.stockinfo.simulationweb.core;

import golf.stockinfo.buysellsimulation.SimulateSET_SET50_CombinedApproach;
import golf.stockinfo.buysellsimulation.SimulationCommonUtils;
import golf.stockinfo.buysellsimulation.SimulationData;

import java.sql.Date;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.TreeSet;

import org.apache.commons.math.stat.regression.SimpleRegression;
import org.joda.time.DateTime;
import org.joda.time.Days;

public class SimulationThread implements Runnable {

	private SimpleRegression SIMPLE_REGRESSION = new SimpleRegression();
	public static TreeSet<SimulationOutput> ALL_OUTPUT = new TreeSet<SimulationOutput>();
	public static long TOTAL_PROFIT_COUNT = 0;
	public static long TOTAL_LOSS_COUNT = 0;
	
	private String currentStatus = "";
	private short smavETF = 8;
	private short smavTFEX = 8;
	private boolean stopThread = false;
	private String threadName = "";

	private Thread simulationThread;

	public SimulationThread(short smavETF, short smavTFEX) {
		this.smavETF = smavETF;
		this.smavTFEX = smavTFEX;
		
		this.threadName = "simulation" + smavETF + ":" + smavTFEX;
		
		// Create a new, second thread
		simulationThread = new Thread(this, this.threadName);
		// System.out.println("Child thread: " + t);
		simulationThread.start(); // Start the thread
	}
	
	public void stopThread(){
		if(simulationThread.isAlive()){
			stopThread = true;
		}
	}
	
	public boolean isThreadAlive(){
		return simulationThread.isAlive();
	}
	
	public String getThreadStatus(){
		String status;
		if(simulationThread.isAlive()){
			status = "running";
		}else{
			status = "stopped";
		}
		
		return threadName + " == " + status + " ==> " + currentStatus;
	}
	
	// This is the entry point for the second thread.
	public void run() {
		try {
			DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException("SQL Error : " + e.getMessage());
		}
		Date[] dateArr = { Date.valueOf("1996-08-30"), Date.valueOf("1996-10-14"), Date.valueOf("1996-11-28"),
				Date.valueOf("1997-01-12"), Date.valueOf("1997-02-26"), Date.valueOf("1997-04-12"), Date.valueOf("1997-05-27"),
				Date.valueOf("1997-07-11"), Date.valueOf("1997-08-25"), Date.valueOf("1997-10-09"), Date.valueOf("1997-11-23"),
				Date.valueOf("1998-01-07"), Date.valueOf("1998-02-21"), Date.valueOf("1998-04-07"), Date.valueOf("1998-05-22"),
				Date.valueOf("1998-07-06"), Date.valueOf("1998-08-20"), Date.valueOf("1998-10-04"), Date.valueOf("1998-11-18"),
				Date.valueOf("1999-01-02"), Date.valueOf("1999-02-16"), Date.valueOf("1999-04-02"), Date.valueOf("1999-05-17"),
				Date.valueOf("1999-07-01"), Date.valueOf("1999-08-15"), Date.valueOf("1999-09-29"), Date.valueOf("1999-11-13"),
				Date.valueOf("1999-12-28"), Date.valueOf("2000-02-11"), Date.valueOf("2000-03-27"), Date.valueOf("2000-05-11"),
				Date.valueOf("2000-06-25"), Date.valueOf("2000-08-09"), Date.valueOf("2000-09-23"), Date.valueOf("2000-11-07"),
				Date.valueOf("2000-12-22"), Date.valueOf("2001-02-05"), Date.valueOf("2001-03-22"), Date.valueOf("2001-05-06"),
				Date.valueOf("2001-06-20"), Date.valueOf("2001-08-04"), Date.valueOf("2001-09-18"), Date.valueOf("2001-11-02"),
				Date.valueOf("2001-12-17"), Date.valueOf("2002-01-31"), Date.valueOf("2002-03-17"), Date.valueOf("2002-05-01"),
				Date.valueOf("2002-06-15"), Date.valueOf("2002-07-30"), Date.valueOf("2002-09-13"), Date.valueOf("2002-10-28"),
				Date.valueOf("2002-12-12"), Date.valueOf("2003-01-26"), Date.valueOf("2003-03-12"), Date.valueOf("2003-04-26"),
				Date.valueOf("2003-06-10"), Date.valueOf("2003-07-25"), Date.valueOf("2003-09-08"), Date.valueOf("2003-10-23"),
				Date.valueOf("2003-12-07"), Date.valueOf("2004-01-21"), Date.valueOf("2004-03-06"), Date.valueOf("2004-04-20"),
				Date.valueOf("2004-06-04"), Date.valueOf("2004-07-19"), Date.valueOf("2004-09-02"), Date.valueOf("2004-10-17"),
				Date.valueOf("2004-12-01"), Date.valueOf("2005-01-15"), Date.valueOf("2005-03-01"), Date.valueOf("2005-04-15"),
				Date.valueOf("2005-05-30"), Date.valueOf("2005-07-14"), Date.valueOf("2005-08-28"), Date.valueOf("2005-10-12"),
				Date.valueOf("2005-11-26"), Date.valueOf("2006-01-10"), Date.valueOf("2006-02-24"), Date.valueOf("2006-04-10"),
				Date.valueOf("2006-05-25"), Date.valueOf("2006-07-09"), Date.valueOf("2006-08-23"), Date.valueOf("2006-10-07"),
				Date.valueOf("2006-11-21"), Date.valueOf("2007-01-05"), Date.valueOf("2007-02-19"), Date.valueOf("2007-04-05"),
				Date.valueOf("2007-05-20"), Date.valueOf("2007-07-04"), Date.valueOf("2007-08-18"), Date.valueOf("2007-10-02"),
				Date.valueOf("2007-11-16"), Date.valueOf("2007-12-31"), Date.valueOf("2008-02-14"), Date.valueOf("2008-03-30"),
				Date.valueOf("2008-05-14"), Date.valueOf("2008-06-28"), Date.valueOf("2008-08-12"), Date.valueOf("2008-09-26"),
				Date.valueOf("2008-11-10"), Date.valueOf("2008-12-25"), Date.valueOf("2009-02-08"), Date.valueOf("2009-03-25"),
				Date.valueOf("2009-05-09"), Date.valueOf("2009-06-23"), Date.valueOf("2009-08-07"), Date.valueOf("2009-09-21"),
				Date.valueOf("2009-11-05"), Date.valueOf("2009-12-20"), Date.valueOf("2010-02-03"), Date.valueOf("2010-03-20"),
				Date.valueOf("2010-05-04"), Date.valueOf("2010-06-18"), Date.valueOf("2010-08-02"), Date.valueOf("2010-09-16"),
				Date.valueOf("2010-10-31"), Date.valueOf("2010-12-15"), Date.valueOf("2011-01-29"), Date.valueOf("2011-03-15"),
				Date.valueOf("2011-04-29"), Date.valueOf("2011-06-13"), Date.valueOf("2011-07-28"), Date.valueOf("2011-09-11"),
				Date.valueOf("2011-10-26"), Date.valueOf("2011-12-10"), Date.valueOf("2012-01-24"), Date.valueOf("2012-03-09"),
				Date.valueOf("2012-04-23") };

		double initialCapital = 500000;
		long totalCount = 0;
		long count = 0;
		SimulateSET_SET50_CombinedApproach simulation = new SimulateSET_SET50_CombinedApproach();

		//Count total first

		boolean[] allBooleans = { true, false };
		for (boolean playLong : allBooleans) {
			for (boolean playShort : allBooleans) {
				for (boolean playETFonLong : allBooleans) {
					for (double maxLossOnBalanceForTFEX = -0.7; maxLossOnBalanceForTFEX <= -0.1; maxLossOnBalanceForTFEX = maxLossOnBalanceForTFEX + 0.1) {
						for (double maxLossOnBalanceForETF = -0.7; maxLossOnBalanceForETF <= -0.1; maxLossOnBalanceForETF = maxLossOnBalanceForETF + 0.1) {
							for (double lossStopThresholdPerContract = -10000; lossStopThresholdPerContract < -1000; lossStopThresholdPerContract = lossStopThresholdPerContract + 1000) {
								for (int cappedContractNumber = 5; cappedContractNumber < 200;) {
									for (double profitTakingThresholdETF = 0.05; profitTakingThresholdETF <= 0.5; profitTakingThresholdETF = profitTakingThresholdETF + 0.025) {
										for (double profitTakingThresholdTFEX = 0.05; profitTakingThresholdTFEX <= 0.5; profitTakingThresholdTFEX = profitTakingThresholdTFEX + 0.025) {
											for (boolean profitTaking : allBooleans) {
												for (boolean isContinousInvesting : allBooleans) {
													for (double allowedMinimumRunningBalance = 20000; allowedMinimumRunningBalance <= 100000; allowedMinimumRunningBalance = allowedMinimumRunningBalance + 20000) {
														
														for (Date startDate : dateArr) {
															totalCount++;
														}
														
													}

												}

											}
											
											this.setCurrentStatus("Current counting up to : "
															+ SimulationCommonUtils
																	.formatInteger(totalCount));


										}

									}

									if (cappedContractNumber < 11) {
										cappedContractNumber++;
									} else if (cappedContractNumber < 51) {
										cappedContractNumber = cappedContractNumber + 10;
									} else if (cappedContractNumber < 1000) {
										cappedContractNumber = cappedContractNumber + 50;
									}
								}

							}

						}

					}

				}

			}

		}

		if (smavTFEX < 8) {
			smavTFEX++;
		} else if (smavTFEX < 12) {
			smavTFEX = (short) (smavTFEX + 2);
		} else if (smavTFEX < 50) {
			smavTFEX = (short) (smavTFEX + 4);
		}
		
		

		for (boolean playLong : allBooleans) {
			if(stopThread) break;
			for (boolean playShort : allBooleans) {
				if(stopThread) break;
				for (boolean playETFonLong : allBooleans) {
					if(stopThread) break;
					for (double maxLossOnBalanceForTFEX = -0.7; maxLossOnBalanceForTFEX <= -0.1; maxLossOnBalanceForTFEX = maxLossOnBalanceForTFEX + 0.1) {
						if(stopThread) break;
						for (double maxLossOnBalanceForETF = -0.7; maxLossOnBalanceForETF <= -0.1; maxLossOnBalanceForETF = maxLossOnBalanceForETF + 0.1) {
							if(stopThread) break;
							for (double lossStopThresholdPerContract = -10000; lossStopThresholdPerContract < -1000; lossStopThresholdPerContract = lossStopThresholdPerContract + 1000) {
								if(stopThread) break;
								for (int cappedContractNumber = 5; cappedContractNumber < 200;) {
									if(stopThread) break;
									for (double profitTakingThresholdETF = 0.05; profitTakingThresholdETF <= 0.5; profitTakingThresholdETF = profitTakingThresholdETF + 0.025) {
										if(stopThread) break;
										for (double profitTakingThresholdTFEX = 0.05; profitTakingThresholdTFEX <= 0.5; profitTakingThresholdTFEX = profitTakingThresholdTFEX + 0.025) {
											if(stopThread) break;
											for (boolean profitTaking : allBooleans) {
												if(stopThread) break;
												for (boolean isContinousInvesting : allBooleans) {
													if(stopThread) break;
													for (double allowedMinimumRunningBalance = 20000; allowedMinimumRunningBalance <= 100000; allowedMinimumRunningBalance = allowedMinimumRunningBalance + 20000) {
														if(stopThread) break;
														
														ArrayList<Float> endBalances = new ArrayList<Float>();
														ArrayList<Double> effectiveInterests = new ArrayList<Double>();
														SimulationOutput simOutput = new SimulationOutput(initialCapital, smavETF, smavTFEX, playLong, playShort, playETFonLong, maxLossOnBalanceForTFEX, maxLossOnBalanceForETF, lossStopThresholdPerContract, cappedContractNumber, profitTakingThresholdETF, profitTakingThresholdTFEX, profitTaking, isContinousInvesting, allowedMinimumRunningBalance);
														int lossCount = 0;
														
														for (Date startDate : dateArr) {
															if(stopThread) break;

															ArrayList<SimulationData> output = simulation
																	.execute(false, initialCapital, smavETF,
																			smavTFEX, startDate, 20, playLong,
																			playShort, playETFonLong,
																			maxLossOnBalanceForTFEX,
																			maxLossOnBalanceForETF,
																			lossStopThresholdPerContract,
																			cappedContractNumber,
																			profitTakingThresholdETF,
																			profitTakingThresholdTFEX,
																			profitTaking, isContinousInvesting,
																			allowedMinimumRunningBalance);
															
															if(output.get(output.size() - 1)
																		.getBalance() < initialCapital){
																lossCount++;
																TOTAL_LOSS_COUNT++;
															}else{
																TOTAL_PROFIT_COUNT++;
															}
															
															if(lossCount <= 1){
																
																try {
																	endBalances.add(output.get(output.size() - 1)
																			.getBalance());
																	effectiveInterests.add(SimulationCommonUtils.getApproxEquivalentInterestPerYear(
																			initialCapital,
																			output.get(output.size() - 1).getBalance_asDouble(),
																			Days.daysBetween(
																					new DateTime(output.get(0)
																							.getStartDate()),
																					new DateTime(output.get(
																							output.size() - 1)
																							.getEndDate())).getDays()));
																} catch (IndexOutOfBoundsException e) {
																	
																}

															}
															
															if (count % 10000 == 0) {
																if (count < 30000) {
																	this.setCurrentStatus(new Timestamp(System
																							.currentTimeMillis())
																							.toString()
																							+ " ; "
																							+ SimulationCommonUtils
																									.formatInteger(count)
																							+ " of "
																							+ SimulationCommonUtils
																									.formatInteger(totalCount));
																} else if (count >= 30000 && count < 60000) {
																	SIMPLE_REGRESSION.addData(count,
																			System.currentTimeMillis());
																	this.setCurrentStatus(new Timestamp(System
																							.currentTimeMillis())
																							.toString()
																							+ " ; "
																							+ SimulationCommonUtils
																									.formatInteger(count)
																							+ " of "
																							+ SimulationCommonUtils
																									.formatInteger(totalCount));
																} else {
																	if (count % 100000 == 0)
																		SIMPLE_REGRESSION.addData(count,
																				System.currentTimeMillis());
																	double m = SIMPLE_REGRESSION.getSlope();
																	double b = SIMPLE_REGRESSION.getIntercept();
																	long y = (long) (m * totalCount + b);
																	this.setCurrentStatus(new Timestamp(System
																							.currentTimeMillis())
																							.toString()
																							+ " ; "
																							+ SimulationCommonUtils
																									.formatInteger(count)
																							+ " of "
																							+ SimulationCommonUtils
																									.formatInteger(totalCount)
																							+ " ; ETA="
																							+ new Timestamp(y)
																									.toString());
																}

//																if (count > 1) {
//																	try {
//																		SimulateSET_SET50_CombinedApproach
//																				.getConnection().commit();
//																	} catch (SQLException e) {
//																		e.printStackTrace();
//																		throw new RuntimeException("SQL Error : "
//																				+ e.getMessage());
//																	}
//																}
															}
															count++;
														}
														
														if(lossCount <= 1){
															double avgBalance = 0;
															for(Float endBalance:endBalances){
																avgBalance = avgBalance + endBalance;
															}
															avgBalance = avgBalance / endBalances.size();
															
															double avgIntrst = 0;
															for(double intrst:effectiveInterests){
																avgIntrst = avgIntrst + intrst;
															}
															avgIntrst = avgIntrst / effectiveInterests.size();
															
															simOutput.setAverageEndBalance(avgBalance);
															simOutput.setAverageEffectiveInterestRate(avgIntrst);
															
															synchronized (ALL_OUTPUT) {
																if(ALL_OUTPUT.size() < 100){
																	ALL_OUTPUT.add(simOutput);
																}else{
																	if(ALL_OUTPUT.last().getAverageEffectiveInterestRate()
																			< simOutput.getAverageEffectiveInterestRate()){
																		ALL_OUTPUT.remove(ALL_OUTPUT.first());
																		ALL_OUTPUT.add(simOutput);
																	}
																}
															}
														}
														
													}

												}

											}

										}

									}

									if (cappedContractNumber < 11) {
										cappedContractNumber++;
									} else if (cappedContractNumber < 51) {
										cappedContractNumber = cappedContractNumber + 10;
									} else if (cappedContractNumber < 1000) {
										cappedContractNumber = cappedContractNumber + 50;
									}
								}

							}

						}

					}

				}

			}

		}

		if (smavTFEX < 8) {
			smavTFEX++;
		} else if (smavTFEX < 12) {
			smavTFEX = (short) (smavTFEX + 2);
		} else if (smavTFEX < 50) {
			smavTFEX = (short) (smavTFEX + 4);
		}

	

	}

	public String getCurrentStatus() {
		return currentStatus;
	}

	public void setCurrentStatus(String currentStatus) {
		this.currentStatus = currentStatus;
	}

	public String getThreadName() {
		return threadName;
	}

	public void setThreadName(String threadName) {
		this.threadName = threadName;
	}
}
